﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
using System.Collections;

namespace Model
{
	/// <summary>
	/// Этот класс содержит список одиночных воспроизводимых звуков и повторяемых звуков, а также файл со списком расположения аудио файлов
	/// </summary>
	/// <remarks>
	/// Инициализирует строку (string) хранилища размещений файлов 
	/// </remarks>
   public class Storage
   {
      List<OnetimeSound> onetimeSounds;
      List<RepeatedSound> repeatedSounds;
      string XmlStorageFileLocation;

		/// <summary>
		/// Этот метод определяет список одиночных воспроизводимых звуков и повторяемых звуков, а также файл со списком расположения аудио файлов
		/// </summary>
		/// <remarks>
		/// создается экземпляр списка одиночных воспроизводящихся звуков 
		/// создается экземпляр списка повторяющихся воспроизводящихся звуков 
		/// </remarks>
      public Storage()
      {
         onetimeSounds = new List<OnetimeSound>();
         repeatedSounds = new List<RepeatedSound>();
         XmlStorageFileLocation = Properties.Settings.Default.XmlStorageFileLocation;
      }

		/// <summary>
		/// Этот метод сохраняет все считанные звуки, для этого использует список одиночно воспроизводимых звуков и повторяющихся звуков 
		/// </summary>
		/// <remarks>
		/// Создает экземпляр документа для списка одиночных воспроизводящихся звуков и повторно воспроизводящихся звуков
		/// </remarks>
      public void SaveAllSounds(List<OnetimeSound> onetimeSounds, List<RepeatedSound> repeatedSounds)
      {
         XDocument document = new XDocument();
         XElement soundsInXml = GetSoundsInXml(onetimeSounds, repeatedSounds);
         document.Add(soundsInXml);
         document.Save(XmlStorageFileLocation);
      }

		/// <summary>
		/// Этот метод считывает все звуки 
		/// </summary>
		/// <remarks>
		/// Создает экземпляр XElement для считывания звуков
		/// </remarks>
		/// <returns>Xml список звуков</returns>
      private XElement GetSoundsInXml(List<OnetimeSound> onetimeSounds, List<RepeatedSound> repeatedSounds)
      {
         XElement onetimeSoundsInXml = GetOnetimeSoundsInXml(onetimeSounds);
         XElement repeatedSoundsInXml = GetRepeatedSoundsInXml(repeatedSounds);
         XElement soundsInXml = new XElement("Sounds",
            onetimeSoundsInXml, repeatedSoundsInXml);
         return soundsInXml;
      }

		/// <summary>
		/// Этот метод записывает в список одиночные звуки и элементы с их параметрами
		/// </summary>
		/// <param name=" Name ">Имя звука, символьный тип</param>
		/// <param name=" XmlParameterName ">Параметр имени</param>
		/// <param name=" XmlParameterValue ">Размер файла, в мб</param>
		/// <param name=" SoundFileLocation ">Размещение файла</param>
		/// <param name=" MaxDuration ">Максимальная длина, в миллисекундах</param>
		/// <param name=" SpecifiedVolume ">Громкость, целое число, о до ??</param>
		/// <param name=" SpecifiedFrequency ">Частота, целое число, о до ??</param>
		/// <remarks>
		/// Создает список для экземпляров XElement для одиночно воспроизводящегося звука и его параметров
		/// </remarks>
		/// <returns>одиночно воспроизводящегося звука в Xml</returns>
      private XElement GetOnetimeSoundsInXml(List<OnetimeSound> onetimeSounds)
      {
         List<XElement> soundsInXmlCollection = new List<XElement>();
         foreach (OnetimeSound sound in onetimeSounds)
         {
            XElement soundInXml = new XElement("OnetimeSound",
               new XElement("Name", sound.Name),
               new XElement("XmlParameterName", sound.XmlParameterName),
               new XElement("XmlParameterValue", sound.XmlParameterValue),
               new XElement("SoundFileLocation", sound.SoundFileLocation),
               new XElement("MaxDuration", sound.MaxDuration),
               new XElement("BlockRepeat", sound.BlockRepeat),
               new XElement("SpecifiedVolume", sound.SpecifiedVolume),
               new XElement("SpecifiedFrequency", sound.SpecifiedFrequency));
            soundsInXmlCollection.Add(soundInXml);
         }
         XElement onetimeSoundsInXml = new XElement("OnetimeSounds", soundsInXmlCollection);
         return onetimeSoundsInXml;
      }

		/// <summary>
		/// Этот метод повторяющиеся звуки и их параметры
		/// </summary>
		/// <remarks>
		/// Проводит сортировку по громкости и частоте
		/// </remarks>
		/// <param name=" Name ">Имя звука, символьный тип</param>
		/// <param name=" NameOfXmlParameterForVolume ">Имя для Xml параметров громкости</param>
		/// <param name=" NameOfXmlParameterForFrequency "> Имя для Xml параметров частоты</param>
		/// <param name=" ValueOfXmlParameterForVolume ">Значение громкости</param>
		/// <param name=" ValueOfXmlParameterForFrequency ">Значение Частоты</param>
		/// <param name=" SoundFileLocation ">Размещение файла</param>
		/// <remarks>
		/// Создает список для экземпляров XElement для повторно воспроизводящегося звука и его параметров
		/// </remarks>
		/// <returns> повторно воспроизводящегося звука в Xml</returns>
      private XElement GetRepeatedSoundsInXml(List<RepeatedSound> repeatedSounds)
      {
         List<XElement> soundsInXmlCollection = new List<XElement>();
         foreach (RepeatedSound sound in repeatedSounds)
         {
				XElement tableOfValuesForVolumeInXml = GetTableOfValuesForVolumeInXml(sound.TableOfValuesForVolume);
				XElement tableOfValuesForFrequencyInXml = GetTableOfValuesForFrequencyInXml(sound.TableOfValuesForFrequency);

            XElement soundInXml = new XElement("RepeatedSound",
               new XElement("Name", sound.Name),
               new XElement("NameOfXmlParameterForVolume", sound.NameOfXmlParameterForVolume),
               new XElement("NameOfXmlParameterForFrequency", sound.NameOfXmlParameterForFrequency),
               new XElement("ValueOfXmlParameterForVolume", sound.ValueOfXmlParameterForVolume),
               new XElement("ValueOfXmlParameterForFrequency", sound.ValueOfXmlParameterForFrequency),
               new XElement("SoundFileLocation", sound.SoundFileLocation),
               tableOfValuesForVolumeInXml, tableOfValuesForFrequencyInXml);

            soundsInXmlCollection.Add(soundInXml);
         }
         XElement repeatedSoundsInXml = new XElement("RepeatedSounds", soundsInXmlCollection);
         return repeatedSoundsInXml;
      }

		/// <summary>
		/// Этот метод создает таблицу значений громкости
		/// </summary>
		/// <remarks>
		/// Использует отсортированные списки
		/// Использует ключи
		/// </remarks>
		/// <param name="Entry ">Вход</param>
		/// <param name="Parameter">Параметр для ключа</param>
		/// <param name="Volume ">Громкость, целое от 0 до 100</param>
		/// <returns> таблицу значений громкости в Xml</returns>
      private XElement GetTableOfValuesForVolumeInXml(SortedList<double, double> tableOfValuesForVolume)
      {
         IList<double> keys = tableOfValuesForVolume.Keys;
         IList<double> values = tableOfValuesForVolume.Values;
         List<XElement> entries = new List<XElement>();
         foreach (Double key in keys)
         {
            int indexOfKey = keys.IndexOf(key);
            XElement entry = new XElement("Entry",
               new XElement("Parameter", key),
               new XElement("Volume", values[indexOfKey]));
            entries.Add(entry);
         }
         XElement tableOfValuesForVolumeInXml = new XElement("TableOfValuesForVolume", entries);
         return tableOfValuesForVolumeInXml;
      }

		/// <summary> 
		/// Этот метод создает таблицу значений частоты
		/// </summary>
		/// <remarks>
		/// Использует отсортированные списки
		/// Использует ключи
		/// </remarks>
		/// <param name="Entry ">Вход</param>
		/// <param name="Parameter">Параметр для ключа</param>
		/// <param name="Frequency ">Частота, целое от 0 до 100</param>
		/// <returns> таблицу значений частоты в Xml</returns>
      private XElement GetTableOfValuesForFrequencyInXml(SortedList<double, double> tableOfValuesForFrequency)
      {
         IList<double> keys = tableOfValuesForFrequency.Keys;
         IList<double> values = tableOfValuesForFrequency.Values;
         List<XElement> entries = new List<XElement>();
         foreach (Double key in keys)
         {
            int indexOfKey = keys.IndexOf(key);
            XElement entry = new XElement("Entry",
               new XElement("Parameter", key),
               new XElement("Frequency", values[indexOfKey]));
            entries.Add(entry);
         }
         XElement tableOfValuesForFrequencyInXml = new XElement("TableOfValuesForFrequency", entries);
         return tableOfValuesForFrequencyInXml;
      }

		/// <summary> 
		/// Этот метод загружает документ из корня (восстанавливает его)
		/// </summary>
      public void Restore()
      {
         XDocument document = XDocument.Load(XmlStorageFileLocation);
         XElement soundsInXml = document.Root;
         ParseSoundsInXml(soundsInXml);
      }

		/// <summary> 
		/// Этот метод структурирует звуковые элементы 
		/// </summary>
		/// <param name=" OnetimeSounds">Имя одно воспроизводящегося звука</param>
		/// <param name=" RepeatedSounds "> Имя повторно воспроизводящегося звука</param>
      private void ParseSoundsInXml(XElement soundsInXml)
      {
         XElement onetimeSoundsInXml = soundsInXml.Element("OnetimeSounds");
         ParseOnetimeSoundsInXml(onetimeSoundsInXml);
         XElement repeatedSoundsInXml = soundsInXml.Element("RepeatedSounds");
         ParseRepeatedSoundsInXml(repeatedSoundsInXml);
      }

		/// <summary> 
		/// Этот метод структурирует одно воспроизводящиеся звуки
		/// </summary>
		/// <remarks>
		/// Использует список одно воспроизводящихся звуков
		/// </remarks>
		/// <param name=" OnetimeSound ">Имя одно воспроизводящегося звука</param>
		/// <param name=" Name "> Имя элемента</param>
		/// <param name=" XmlParameterName "> Параметр имени</param>
		/// <param name=" XmlParameterValue "> Размер, действительное число</param>
		/// <param name=" SoundFileLocation "> Размещение файла</param>
		/// <param name=" MaxDuration "> Максимальна длина, действительное число</param>
		/// <param name=" BlockRepeat "> Повтор в блоке, логическое (1 или 0)</param>
		/// <param name=" SpecifiedVolume "> Специальная громкость, действительное число</param>
		/// <param name=" SpecifiedFrequency "> Специальная частота, действительное число </param>
      private void ParseOnetimeSoundsInXml(XElement onetimeSoundsInXml)
      {
         foreach (XElement soundInXml in onetimeSoundsInXml.Elements("OnetimeSound"))
         {
            OnetimeSound sound = new OnetimeSound()
            {
               Name = soundInXml.Element("Name").Value,
               XmlParameterName = soundInXml.Element("XmlParameterName").Value,
               XmlParameterValue = Double.Parse(soundInXml.Element("XmlParameterValue").Value.Replace('.', ',')),
               SoundFileLocation = soundInXml.Element("SoundFileLocation").Value,
               MaxDuration = uint.Parse(soundInXml.Element("MaxDuration").Value.Replace('.', ',')),
               BlockRepeat = Boolean.Parse(soundInXml.Element("BlockRepeat").Value),
               SpecifiedVolume = Double.Parse(soundInXml.Element("SpecifiedVolume").Value.Replace('.', ',')),
               SpecifiedFrequency = Double.Parse(soundInXml.Element("SpecifiedFrequency").Value.Replace('.', ',')),
            };
            onetimeSounds.Add(sound);
         }
      }

		/// <summary> 
		/// Этот метод структурирует повторяемый звук
		/// </summary>
		/// <remarks>
		/// Использует список повторно воспроизводящихся звуков
		/// </remarks>
		/// <param name=" RepeatedSound ">Имя повторно воспроизводящегося звука</param>
		/// <param name=" Name "> Имя элемента</param>
		/// <param name=" NameOfXmlParameterForVolume "> Имя параметра громкости</param>
		/// <param name=" ValueOfXmlParameterForVolume "> Громкость, действительное число</param>
		/// <param name=" ValueOfXmlParameterForFrequency "> Частота, действительное число</param>
		/// <param name=" SoundFileLocation ">Размещение файла</param>
      private void ParseRepeatedSoundsInXml(XElement repeatedSoundsInXml)
      {
         foreach (XElement soundInXml in repeatedSoundsInXml.Elements("RepeatedSound"))
         {
            RepeatedSound sound = new RepeatedSound()
            {
               Name = soundInXml.Element("Name").Value,
               NameOfXmlParameterForVolume = soundInXml.Element("NameOfXmlParameterForVolume").Value,
               NameOfXmlParameterForFrequency = soundInXml.Element("NameOfXmlParameterForFrequency").Value,
               ValueOfXmlParameterForVolume = Double.Parse(soundInXml.Element("ValueOfXmlParameterForVolume").Value.Replace('.', ',')),
               ValueOfXmlParameterForFrequency = Double.Parse(soundInXml.Element("ValueOfXmlParameterForFrequency").Value.Replace('.', ',')),
               SoundFileLocation = soundInXml.Element("SoundFileLocation").Value
            };

				/// <summary> 
				/// Этот метод сортирует звуков в список по громкости
				/// </summary>
				sound.TableOfValuesForVolume = ParseTableOfValuesForVolumeInXml(soundInXml);
				/// <summary> 
				/// Этот метод сортирует звуки в список по частоте
				/// </summary>
				sound.TableOfValuesForFrequency = ParseTableOfValuesForFrequencyInXml(soundInXml);

            repeatedSounds.Add(sound);
         }
      }

		/// <summary> 
		/// Этот метод создает список для сортировки звуков по громкости
		/// </summary>
		/// <param name=" TableOfValuesForVolume "> Значения громкости</param>
		/// <param name=" Entry "> Вход</param>
		/// <param name=" Parameter "> Параметр элемента, действительное число</param>
		/// <param name=" Volume "> Громкость, действительное число</param>
		/// <returns> таблицу значений громкости</returns>
      private SortedList<double, double> ParseTableOfValuesForVolumeInXml(XElement soundInXml)
      {
         SortedList<double, double> tableOfValuesForVolume = new SortedList<double, double>();
         XElement tableOfValuesForVolumeInXml = soundInXml.Element("TableOfValuesForVolume");
         foreach (XElement entry in tableOfValuesForVolumeInXml.Elements("Entry"))
         {
            double key = Double.Parse(entry.Element("Parameter").Value.Replace('.', ','));
            double value = Double.Parse(entry.Element("Volume").Value.Replace('.', ','));
            tableOfValuesForVolume.Add(key, value);
         }
         return tableOfValuesForVolume;
      }

		/// <summary> 
		/// Этот метод создает список для сортировки звуков по частоте
		/// </summary>
		/// <param name=" TableOfValuesForFrequency "> Значения частоты</param>
		/// <param name=" Entry "> Вход</param>
		/// <param name=" Parameter "> Параметр элемента, действительное число</param>
		/// <param name=" Frequency "> Частота, действительное число</param>
		/// <returns> таблицу значений частоты</returns>
      private SortedList<double, double> ParseTableOfValuesForFrequencyInXml(XElement soundInXml)
      {
         SortedList<double, double> tableOfValuesForFrequency = new SortedList<double, double>();
         XElement tableOfValuesForFrequencyInXml = soundInXml.Element("TableOfValuesForFrequency");
         foreach (XElement entry in tableOfValuesForFrequencyInXml.Elements("Entry"))
         {
            double key = Double.Parse(entry.Element("Parameter").Value.Replace('.', ','));
            double value = Double.Parse(entry.Element("Frequency").Value.Replace('.', ','));
            tableOfValuesForFrequency.Add(key, value);
         }
         return tableOfValuesForFrequency;
      }

		/// <summary> 
		/// Этот метод возвращает одиночный воспроизводящийся звук
		/// </summary>
		/// <returns>Одиночный воспроизводящийся звук.</returns>
      public List<OnetimeSound> GetOnetimeSounds()
      {
         return onetimeSounds;
      }

		/// <summary> 
		/// Этот метод возвращает повторяемый звук
		/// </summary>
		/// <returns>Повторяемый звук.</returns>
      public List<RepeatedSound> GetRepeatedSounds()
      {
         return repeatedSounds;
      }
   }
}
